@crestapps/ai-chat-ui 2.0.0-preview.223 → 2.0.0-preview.225

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai-chat.css CHANGED
@@ -561,3 +561,34 @@ img.medium-zoom-image--opened {
561
561
  .ai-chat-message-item table tbody tr:nth-child(even) {
562
562
  background-color: var(--bs-tertiary-bg, #f8f9fa);
563
563
  }
564
+
565
+ /* Persona avatar shown on assistant messages in place of the robot icon. */
566
+ .ai-chat-assistant-avatar {
567
+ display: block;
568
+ width: 1.5rem;
569
+ height: 1.5rem;
570
+ border-radius: 50%;
571
+ object-fit: cover;
572
+ }
573
+
574
+ .ai-bot-avatar,
575
+ .ai-streaming-avatar {
576
+ display: inline-flex;
577
+ flex-shrink: 0;
578
+ vertical-align: middle;
579
+ }
580
+
581
+ @keyframes ai-chat-avatar-pulse {
582
+ 0%, 100% { opacity: 1; }
583
+ 50% { opacity: 0.45; }
584
+ }
585
+
586
+ .ai-streaming-avatar {
587
+ animation: ai-chat-avatar-pulse 1.5s ease-in-out infinite;
588
+ }
589
+
590
+ @media (prefers-reduced-motion: reduce) {
591
+ .ai-streaming-avatar {
592
+ animation: none;
593
+ }
594
+ }
package/dist/ai-chat.js CHANGED
@@ -13,6 +13,7 @@
13
13
  downloadChartButtonText: "Download",
14
14
  userLabel: "You",
15
15
  assistantLabel: "Assistant",
16
+ assistantAvatarUrl: null,
16
17
  thumbsUpTitle: "Thumbs up",
17
18
  thumbsDownTitle: "Thumbs down",
18
19
  copyTitle: "Click here to copy response to clipboard.",
@@ -46,7 +47,7 @@
46
47
  </div>
47
48
  <div v-else-if="message.role !== 'indicator'" class="ai-chat-message-heading ai-chat-message-heading-assistant">
48
49
  <div :class="getAssistantRoleClasses(message)">
49
- <span :class="getAssistantIconClasses(message, index)"><i :class="getAssistantIcon(message)"></i></span>
50
+ <span :class="getAssistantIconClasses(message, index)"><img v-if="getAssistantAvatarUrl(message)" class="ai-chat-assistant-avatar" :src="getAssistantAvatarUrl(message)" alt="" /><i v-else :class="getAssistantIcon(message)"></i></span>
50
51
  <span class="ai-chat-message-role-label">{{ getAssistantLabel(message) }}</span>
51
52
  </div>
52
53
  </div>
@@ -100,8 +101,8 @@
100
101
  indicatorTemplate: `
101
102
  <div class="ai-chat-message-heading ai-chat-message-heading-assistant">
102
103
  <div class="ai-chat-msg-role ai-chat-msg-role-assistant">
103
- <span class="ai-streaming-icon"><i class="fa fa-robot" style="display: inline-block;"></i></span>
104
- <span class="ai-chat-message-role-label">Assistant</span>
104
+ {{ assistantIcon }}
105
+ <span class="ai-chat-message-role-label">{{ assistantLabel }}</span>
105
106
  </div>
106
107
  </div>
107
108
  `
@@ -445,17 +446,22 @@
445
446
  if (!source || typeof source !== "object") return {};
446
447
  return Object.fromEntries(Object.entries(source).filter(([, value]) => value !== void 0));
447
448
  }
448
- function resolveIndicatorTemplate(template, assistantLabel) {
449
+ function resolveIndicatorTemplate(template, assistantLabel, assistantAvatarUrl) {
449
450
  if (!template) return template;
450
451
  const encodedAssistantLabel = escapeHtmlEntities(assistantLabel || "Assistant");
451
- return template.replace(/\{\{\s*assistantLabel\s*\}\}/g, encodedAssistantLabel);
452
+ const avatarUrl = sanitizeUrl(assistantAvatarUrl);
453
+ const assistantIcon = avatarUrl ? "<span class=\"ai-streaming-avatar\"><img class=\"ai-chat-assistant-avatar\" src=\"" + escapeHtmlAttribute(avatarUrl) + "\" alt=\"\" /></span>" : "<span class=\"ai-streaming-icon\"><i class=\"fa fa-robot\" style=\"display: inline-block;\"></i></span>";
454
+ return template.replace(/\{\{\s*assistantLabel\s*\}\}/g, encodedAssistantLabel).replace(/\{\{\s*assistantIcon\s*\}\}/g, assistantIcon);
455
+ }
456
+ function escapeHtmlAttribute(text) {
457
+ return escapeHtmlEntities(text).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
452
458
  }
453
459
  const initialize = (instanceConfig) => {
454
460
  const normalizedInstanceConfig = compactObject(instanceConfig);
455
461
  const normalizedWidgetConfig = compactObject(normalizedInstanceConfig.widget);
456
462
  const config = Object.assign({}, defaultConfig, normalizedInstanceConfig);
457
463
  config.widget = Object.assign({}, defaultConfig.widget || {}, normalizedWidgetConfig);
458
- config.indicatorTemplate = resolveIndicatorTemplate(config.indicatorTemplate, config.assistantLabel);
464
+ config.indicatorTemplate = resolveIndicatorTemplate(config.indicatorTemplate, config.assistantLabel, config.assistantAvatarUrl);
459
465
  const hasWidgetConfig = !!(normalizedWidgetConfig.chatWidgetContainer && normalizedWidgetConfig.chatWidgetStateName);
460
466
  const widgetBehavior = window.coreAIChatWidgetBehavior || null;
461
467
  defaultConfig = config;
@@ -505,6 +511,7 @@
505
511
  metricsEnabled: !!config.metricsEnabled,
506
512
  userLabel: config.userLabel,
507
513
  assistantLabel: config.assistantLabel,
514
+ assistantAvatarUrl: sanitizeUrl(config.assistantAvatarUrl) || null,
508
515
  thumbsUpTitle: config.thumbsUpTitle,
509
516
  thumbsDownTitle: config.thumbsDownTitle,
510
517
  copyTitle: config.copyTitle,
@@ -756,7 +763,6 @@
756
763
  }
757
764
  for (var m = 0; m < this.uploadErrors.length; m++) {
758
765
  var failedItem = this.uploadErrors[m];
759
- failedItem.fileName;
760
766
  var errorMsg = failedItem.error || "Upload failed";
761
767
  html += "<span class=\"alert alert-danger d-flex align-items-start gap-2 w-100 mb-1 py-1 px-2\" role=\"alert\" style=\"font-size: 0.8rem;\">";
762
768
  html += "<i class=\"fa-solid fa-circle-exclamation mt-1\" style=\"font-size: 0.7rem;\"></i>";
@@ -853,8 +859,13 @@
853
859
  },
854
860
  getAssistantIconClasses(message, index) {
855
861
  var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;
862
+ if (this.getAssistantAvatarUrl(message)) return [this.shouldAnimateAssistantIcon(message, index) ? "ai-streaming-avatar" : "ai-bot-avatar"];
856
863
  return [this.shouldAnimateAssistantIcon(message, index) ? "ai-streaming-icon" : "ai-bot-icon", appearance && appearance.cssClass ? appearance.cssClass : ""];
857
864
  },
865
+ getAssistantAvatarUrl(message) {
866
+ if (!this.assistantAvatarUrl || message && this.normalizeAssistantAppearance(message.appearance)) return null;
867
+ return this.assistantAvatarUrl;
868
+ },
858
869
  getAssistantIcon(message) {
859
870
  var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;
860
871
  return appearance && appearance.icon ? appearance.icon : "fa fa-robot";
@@ -1032,7 +1043,7 @@
1032
1043
  if (this.realtimeController) this.realtimeController.receivePcm(bytes);
1033
1044
  } else this.audioChunks.push(bytes);
1034
1045
  });
1035
- this.connection.on("ReceiveAudioComplete", (sessionId) => {
1046
+ this.connection.on("ReceiveAudioComplete", () => {
1036
1047
  this.playCollectedAudio();
1037
1048
  });
1038
1049
  this.connection.on("ReceiveNotification", (notification) => {
@@ -1420,7 +1431,7 @@
1420
1431
  icons[i].classList.add("ai-bot-icon");
1421
1432
  }
1422
1433
  }
1423
- for (var i = 0; i < this.messages.length; i++) if (this.messages[i].isStreaming) this.messages[i].isStreaming = false;
1434
+ for (i = 0; i < this.messages.length; i++) if (this.messages[i].isStreaming) this.messages[i].isStreaming = false;
1424
1435
  },
1425
1436
  handleExternalTtsStop(e) {
1426
1437
  if (e?.detail?.sourceId === this.ttsInstanceId) return;
@@ -2007,7 +2018,7 @@
2007
2018
  this.generatePrompt(e.target);
2008
2019
  });
2009
2020
  const chatSessions = document.getElementsByClassName("chat-session-history-item");
2010
- for (var i = 0; i < chatSessions.length; i++) chatSessions[i].addEventListener("click", (e) => {
2021
+ for (i = 0; i < chatSessions.length; i++) chatSessions[i].addEventListener("click", (e) => {
2011
2022
  e.preventDefault();
2012
2023
  var sessionId = e.target.getAttribute("data-session-id");
2013
2024
  if (!sessionId) {
@@ -2144,7 +2155,7 @@
2144
2155
  if (msg) this.updateFeedbackIcons(containers[i], msg.userRating);
2145
2156
  }
2146
2157
  },
2147
- rateMessage(message, isPositive, event) {
2158
+ rateMessage(message, isPositive) {
2148
2159
  var sessionId = this.getSessionId();
2149
2160
  if (!sessionId || !message.id || !this.connection) return;
2150
2161
  var newRating = message.userRating === isPositive ? null : isPositive;
@@ -2279,6 +2290,7 @@
2279
2290
  sessionUrlTemplate: getAttributeValue(element, "data-coreai-chat-session-url-template"),
2280
2291
  userLabel: getAttributeValue(element, "data-coreai-chat-user-label"),
2281
2292
  assistantLabel: getAttributeValue(element, "data-coreai-chat-assistant-label"),
2293
+ assistantAvatarUrl: getAttributeValue(element, "data-coreai-chat-assistant-avatar-url"),
2282
2294
  copyTitle: getAttributeValue(element, "data-coreai-chat-copy-title"),
2283
2295
  copiedTitle: getAttributeValue(element, "data-coreai-chat-copied-title")
2284
2296
  };